package tul.cot.util;

import java.util.List;
import java.util.Map.Entry;

import org.apache.log4j.Logger;

import tul.cot.MigStats;
import tul.cot.MigrationTree;
import tul.cot.MigStats.Change;


public class TreeStats2 {
	
	private static final Logger LOG = Logger.getLogger(TreeStats2.class);
	
	private final MigrationTree tree;
	private final double START_TIME;
	private final int SEG_NUM;
	private final double SEG_LENGTH;
	
	private List<MigrationTree> sortNodes;
	private int curr;
	
	private MigStats[] migStats;
	private MigStats globalStats;

	public TreeStats2(MigrationTree tree, double startTime, int segNum) {
		if (tree == null)
			throw new IllegalArgumentException("tree == null");
		if (startTime <= 0)
			throw new IllegalArgumentException("startTime <= 0");
		if (segNum < 1)
			throw new IllegalArgumentException("segNum < 1");
		
		this.tree = tree;
		this.START_TIME = startTime;
		this.SEG_NUM = segNum;
		this.SEG_LENGTH = START_TIME / SEG_NUM;
		
		init();
	}
	
	private void init() {
		sortNodes = TreeUtils.sortNodes(tree);
		migStats = new MigStats[SEG_NUM+2];
		
		globalStats = new MigStats(-1, Double.MAX_VALUE, 0);
		migStats[SEG_NUM+1] = globalStats;
		
		int lines = -1;
		double maxT = -1;
		double minT = -1;
		
		for (int currSegNum=0; currSegNum<=SEG_NUM; ++currSegNum) {
			
			if (currSegNum == 0) {
				curr = 0;
				lines = 1;
				
				maxT = Double.MAX_VALUE;
				minT = START_TIME;
			} else {
				maxT = minT;
				minT = maxT - SEG_LENGTH;
			}
			
			MigStats stats = new MigStats(currSegNum, maxT, minT);
			migStats[currSegNum] = stats;
			
//			LOG.debug(stats.toString());
			
			stats.in = lines;
			stats.out = lines;
			
			while (getCurrNode() != null && getCurrNode().getTime() > stats.tMax) {
				++curr;
			}
			
			while (getCurrNode() != null && getCurrNode().getTime() <= stats.tMax && getCurrNode().getTime() > stats.tMin) {
				
				MigrationTree node = getCurrNode();
				
				stats.out += node.getNumSons() - 1;
				stats.splits++;
				
				if (node.getParent() == null) {
					stats.firstSplits++;
				} else if (node.getParent().getTime() > stats.tMax) {
					stats.firstSplits++;
				}
				
				for (MigrationTree son: node.getSons()) {
					String from	= node.getLabel();
					String to	= son.getLabel();
					if (!from.equals(to)) {
						stats.changes++;
						stats.incChangeNum(from, to, 1);
					} else {
						stats.no_changes++;
					}
				}
				
				++curr;
			}
			
			lines = stats.out;
			
			addStats(globalStats, stats);
		}
		
		globalStats.in = 1;
		globalStats.firstSplits = 0;
		for (int x=0; x<=SEG_NUM; ++x) {
			if (migStats[x].firstSplits > 0) {
				globalStats.firstSplits = migStats[x].firstSplits;
				break;
			}
		}
		globalStats.out = migStats[SEG_NUM].out;
	}

	private void addStats(MigStats global, MigStats stats) {
		global.changes += stats.changes;
		global.no_changes += stats.no_changes;
		global.splits += stats.splits;
		
		for (Entry<Change, Integer> entry: stats.getCounters()){
			Change change = entry.getKey();
			int count = entry.getValue();
			
			global.incChangeNum(change.from, change.to, count);
		}
	}

	private MigrationTree getCurrNode() {
		return curr < sortNodes.size() ? sortNodes.get(curr) : null;
	}

	/**
	 * @param segNum number of segment (-1 means whole tree, 0 means header segment)
	 * @return migration statistics about specified segment
	 */
	public MigStats getMigStats(int segNum) {
		if (segNum < -1 || segNum > SEG_NUM)
			throw new IllegalArgumentException(String.format("segNum value should be from range [-1,%d] (segNum: %d)",
					SEG_NUM,segNum));
		
		return segNum == -1 ? migStats[SEG_NUM + 1] : migStats[segNum];
	}
}
